import math

from pygame.locals import *
import pygame

from pgEngine.point import Point, AxisAlignedBoundingBox

from colors import *


class Toolbox:
    def __init__(self, edit):
        self.edit = edit
        self.window = edit.window
        self.res = edit.map.res
        
        self.toolSelect = self.res.loadImage("tools.png")
        self.toolButtons = self.res.loadImage("toolbox.png")
        
        self.tools = ["rectSelect", "move", "brush", "fill",
                      "rubberStamp", "scale", "pathEdit", "eyeDropper"]

        self.toolModes = {
                "brush": BrushTool,
                "pathEdit": PathEditTool,
                "rectSelect": RectSelectTool,
                "fill": FillTool,
                "move": MoveTool
                }
        
        self.toolKeys = {
            K_m: "rectSelect",
            K_v: "move",
            K_b: "brush",
            K_f: "fill",
            K_r: "rubberStamp",
            K_s: "scale",
            K_t: "pathEdit",
            K_i: "eyeDropper"
        }

        
        self.window.mode.append(self)

        self.curTool = None
        self.setTool("rectSelect")

        # Images
        self.toolBox = None
        self.toolBgr = None
        self.createToolbox()


    def setTool(self, toolName):
        if self.curTool is not None:
            toolMode = []
            while self.window.mode[-1] != self:
                toolMode.insert(0, self.window.mode.pop())
            if len(toolMode) > 0:
                self.toolModes[self.curTool] = toolMode
            
        if toolName in self.toolModes:
            toolMode = self.toolModes[toolName]
            if type(toolMode) is not list:
                # Instantiate the mode.
                oldModes = self.window.mode
                self.window.mode = []
                toolMode = toolMode(self.window, self.edit)
                self.toolModes[toolName] = [toolMode] + self.window.mode
                self.window.mode = oldModes
            self.window.mode.extend(self.toolModes[toolName])
        
        self.curTool = toolName
    
    def getCurTool(self):
        if self.curTool in self.toolModes:
            return self.toolModes[self.curTool][0]
        return None

    def onDrawMapObjects(self, surface):
        rcMenu = self.window.overlayRects["menu"]
        if rcMenu != self.rcMenu:
            self.rcMenu = rcMenu
            self.rcToolbox = None
            del self.rcToolbox
    
    def createToolbox(self):
        tools = {
            "rectSelect": (0,0),
            "ellipSelect": (1,0),
            "lassoSelect": (2,0),
            "wandSelect": (0,1),
            "colorSelect": (1,1),
            "shapeSelect": (2,1),
            "pathEdit": (0,2),
            "eyeDropper": (1,2),
            "zoom": (2,2),
            "ruler": (0,3),
            "move": (1,3),
            "crop": (2,3),
            "rotate": (0,4),
            "scale": (1,4),
            "shear": (2,4),
            "perspective": (0,5),
            "flip": (1,5),
            "text": (2,5),
            "fill": (0,6),
            "autoTile": (1,6),
            "pencil": (2,6),
            "brush": (0,7),
            "eraser": (1,7),
            "airbrush": (2,7),
            "pen": (0,8),
            "rubberStamp": (1,8),
            "blur": (2,8),
            "smudge": (0,9),
            "dodgeBurn": (1,9)
        }
        
        self.toolBox = pygame.Surface((4*24, 2*22), 0, self.toolButtons)
        row = col = 0
        for key in self.tools:
            rcLoc = pygame.Rect(tools[key][0] * 21 + 2,
                                tools[key][1] * 22,
                                24, 22)
            self.toolBox.blit(self.toolButtons.subsurface(rcLoc),
                              (col*24, row*24))
            col = (col + 1) % 4
            if col == 0:
                row += 1
        rcBgr = pygame.Rect(0,219,130,69)
        self.toolBgr = self.toolButtons.subsurface(rcBgr)
        self.toolBgr.set_alpha(160)
        
        self.rcMenu = self.window.overlayRects["menu"]
        
    def onEvent(self, event):
        retVal = None
        
        if (event.type == MOUSEBUTTONDOWN and
            event.button == 1 and
            self.rcToolbox.collidepoint(event.pos)):

            col = (event.pos[0] - self.rcToolbox.left) / 24
            row = (event.pos[1] - self.rcToolbox.top) / 24
            nTool = row * 4 + col
            self.setTool(self.tools[nTool])
            retVal = False
        elif (event.type == KEYDOWN and
              event.key in self.toolKeys and 
              0 == (event.mod & (KMOD_CTRL|KMOD_ALT)) ):
            self.setTool(self.toolKeys[event.key])
            retVal = False

        return retVal
        

    def drawMenu(self):
        if not (self.edit.clipPalette in self.window.modes or
                self.edit.tilePalette in self.window.modes):
            self.edit.drawMenu()
        
        # Draw the toolbox
        if not hasattr(self, 'rcToolbox'):
            self.rcToolbox = pygame.Rect(self.rcMenu.left + 26,
                                         self.rcMenu.bottom - 69,
                                         4 * 24,
                                         2 * 24)

        posBgr = (self.rcToolbox.left - 20,
                  self.rcToolbox.top - 12)
        self.edit.screen.blit(self.toolBgr, posBgr)
        self.edit.screen.blit(self.toolBox, self.rcToolbox.topleft)

        mousePos = (self.window.mouseX, self.window.mouseY)
        if self.rcToolbox.collidepoint(mousePos):
            pass
        
        nTool = self.tools.index(self.curTool)
        row = nTool // 4
        col = nTool % 4
        self.edit.screen.blit(self.toolSelect,
                              (self.rcToolbox.left + col * 24,
                               self.rcToolbox.top + row * 24))
        return False        





class EditTool:
    def __init__(self, window, editMode):
        self.edit = editMode
        self.window = window
        self.map = window.map
        self.game = window.game
        self.ocean = window.game.ocean
        self.selection = editMode.selection
        self.screen = self.window.screen
    
class ClipTool(EditTool):
    def __init__(self, window, editMode):
        EditTool.__init__(self, window, editMode)
        self.palette = editMode.clipPalette
        window.mode.append(self.palette)

class TileTool(EditTool):
    def __init__(self, window, editMode):
        EditTool.__init__(self, window, editMode)
        self.palette = editMode.tilePalette
        window.mode.append(self.palette)
#

class PathEditTool(EditTool):

    def onDrawMapObjects(self, surface):
        if self.window.mode[-1] != self:
            return None

        if not hasattr(self, 'chighlight'):
            self.chighlight = self.map.res.loadImage("circleHighlight.png")
            
        mousePos = (self.window.mouseX, self.window.mouseY)
        if self.window.checkOverlayClick(mousePos):
            rcPos = pygame.Rect((0,0), self.chighlight.get_size())
            rcPos.center = mousePos
            surface.blit(self.chighlight, rcPos)


class BrushTool(TileTool):
    def __init__(self, window, editMode):
        TileTool.__init__(self, window, editMode)
        self.ehighlight = self.map.createHighlight(self.map.tileSize.pos)

 
    def onDrawMapObjects(self, surface):
        # Draw the current hover position
        mousePos = (self.window.mouseX, self.window.mouseY)
        if self.window.checkOverlayClick(mousePos):
            mouseTile = Point(*self.map.getMousePos(grid=False))
            tilePos = self.map.getTilePos(mouseTile)
            surface.blit(self.ehighlight, tilePos.pos)
            return None
            
    def do_brush(self, tilePos, button, keymods):
        if keymods & KMOD_CTRL or button == 2:
            tileName = self.game.ocean.tiles.get(tilePos, "water")
            self.palette.selectPageAndTile(tileName)
        else:
            self.edit.setTile(button,
                              tilePos[0], tilePos[1],
                              self.palette.tileName)

    def onEvent(self, event):
        retval = None
        
        if not (hasattr(event, 'pos') and
                self.window.checkOverlayClick(event.pos)):
            pass

        elif event.type == MOUSEBUTTONDOWN:
            if event.button in (1,2,3):
                mousePos = self.map.getMousePos(grid=False)
                tileX = int(math.floor(mousePos[0]))
                tileY = int(math.floor(mousePos[1]))
                keyMods = pygame.key.get_mods() 
                self.do_brush((tileX, tileY), event.button, keyMods)
                retval = False

        elif event.type == MOUSEMOTION:
            tileX, tileY = self.map.getMousePos(grid=False)
            keymods = pygame.key.get_mods()

            if event.buttons[0] == 1:
                self.do_brush((tileX, tileY), 1, keymods)
                retval = False
            elif event.buttons[1] == 1:
                self.do_brush((tileX, tileY), 2, keymods)
                retval = False                
            elif event.buttons[2] == 1:
                self.do_brush((tileX, tileY), 3, keymods)
                retval = False
        
        return retval
#




class MoveTool(ClipTool):
    dragSrc = None
    moveSrc = None
    moveSelectionId = None
    
    def do_moveSelection(self, dest, button, keymods):
        dist = dest - self.dragSrc
        if dist.length == 0:
            return
        print dist
        self.dragSrc = dest
        for n in range(len(self.selection)):
            self.selection[n] += dist

    def onEvent(self, event):
        if not (hasattr(event, 'pos') and
                self.window.checkOverlayClick(event.pos)):
            return None
        
        tileX, tileY = self.map.getMousePos(grid=False)
        keymods = pygame.key.get_mods()
        if (event.type == MOUSEMOTION and 1 in event.buttons[0:3]):
            if event.buttons[0] == 1:
                button = 1
            elif event.buttons[2] == 1:
                button = 3
            else:
                button = 2
            
            keymods = pygame.key.get_mods()
            
            dragDest = Point(tileX, tileY)
            self.do_moveSelection(dragDest, button, keymods)
            return False
        
        elif event.type == MOUSEBUTTONDOWN and event.button in (1,3):
            dragSrc = Point(tileX, tileY)

            if len(self.selection) == 0 and event.button == 1:
                self.selection[:] = [dragSrc,dragSrc,dragSrc,dragSrc]
                # Copy to the clip list
                if keymods & KMOD_CTRL:
                    self.palette.copy()
                else:
                    self.palette.cut()
                
            elif event.button == 3:
                # Paste from the clip list
                self.palette.paste()
            else:
                # Selection, left click
                item = self.palette.getCurItem()
                if (item is None or
                    self.moveSelectionId != self.edit.selectionId):
                    self.moveSrc = None

                if self.moveSrc is None:
                    if keymods & KMOD_CTRL:
                        self.palette.copy()
                    else:
                        self.palette.cut()
                
            self.dragSrc = dragSrc
            # Bottom left of selection in tile units
            if self.moveSrc is None:
                self.moveSrc = self.selection[2]
                self.moveSelectionId = self.edit.selectionId
            return False
    
        return None
        
    def onDrawMapObjects(self, surface):
        self.edit.drawFloatingSel(surface)
        self.edit.drawMarchingAnts(surface)



class RectSelectTool(ClipTool):
    def onDrawMapObjects(self, surface):
        self.edit.drawMarchingAnts(surface)


    def do_rectSelect(self, tilePos, button, keymods, drag=False):
        
        if button == 3:
            self.edit.clearSelection()
            return
        
        topleft = Point(*tilePos)
        #tilePos = self.map.getTilePos(topleft)
        if len(self.selection) == 0 or not drag:
            self.edit.clearSelection()
            self.selection[:] = [topleft,
                              topleft,
                              0, 0]
        else:
            self.selection[1] = topleft
        
        minX = min(self.selection[0].x, topleft.x)
        minY = min(self.selection[0].y, topleft.y)
        maxX = max(self.selection[0].x, topleft.x)
        maxY = max(self.selection[0].y, topleft.y)
        
        self.selection[2] = Point(minX, minY, int)
        self.selection[3] = Point(maxX, maxY, int)
        
        #self.edit.selection = self.selection
        
    def do_moveSelect(self, dest, button, keymods):
        dist = dest - self.dragSrc

        if dist.length == 0:
            return
        
        self.dragSrc = dest
        
        for n in range(4):
            self.selection[n] += dist

    def onEvent(self, event):
    
        if not ( hasattr(event, 'pos') and
                 self.window.checkOverlayClick(event.pos) ):
            return None

        tileX, tileY = self.map.getMousePos(grid=False)
        keymods = pygame.key.get_mods()
        if (event.type == MOUSEMOTION and 1 in event.buttons[0:3]):

            if event.buttons[0] == 1:
                button = 1
            elif event.buttons[2] == 1:
                button = 3
                
            if self.dragSrc is not None:
                # Move the rect.
                dragDest = Point(*self.map.getMousePos(grid=False))
                self.do_moveSelect(dragDest, button, keymods)
                return False

            self.do_rectSelect((tileX, tileY), button, keymods, True)
            return False
        
        elif event.type == MOUSEBUTTONDOWN and event.button in (1,3):

            if len(self.selection) == 4 and event.button == 1:
                minX, minY = self.selection[2].pos
                maxX, maxY = self.selection[3].pos
    
                if minX <= tileX <= maxX and minY <= tileY <= maxY:
                    self.dragSrc = Point(*self.map.getMousePos(grid=False))
                    return False
            
            self.dragSrc = None
            self.do_rectSelect((tileX, tileY), event.button, keymods, False)
            return False
    
        return None



class FillTool(TileTool):
    def _fill(self, x, y, minX, minY, maxX, maxY, newTile):
        tiles = self.game.ocean.tiles
        oldTile = tiles.get((x, y), "water")
        
        if oldTile == newTile:
            return
        
        stack = []
        stack.append((x, y))
        
        while len(stack):
            x, y = stack.pop()
            y1 = y
            while tiles.get((x,y1),"water") == oldTile and y1 >= minY: y1 -= 1
            y1 += 1
            spanLeft = spanRight = False
            while tiles.get((x,y1),"water") == oldTile and y1 <= maxY:
                tiles[(x,y1)] = newTile
                # Go left.
                if (not spanLeft and x > minX and
                    tiles.get((x-1,y1), "water") == oldTile):
                    stack.append((x-1,y1))
                    spanLeft = True
                elif (spanLeft and x > minX and
                      tiles.get((x-1,y1), "water") != oldTile):
                    spanLeft = False
                # Go right.
                if (not spanRight and x < maxX and
                    tiles.get((x+1,y1), "water") == oldTile):
                    stack.append((x+1,y1))
                    spanRight = True
                elif (spanRight and x < maxX and
                      tiles.get((x+1,y1), "water") != oldTile):
                    spanRight = False
                y1 += 1

        self.ocean.dirty = True
        self.window.map.dirty = True
    
    def do_fillSelection(self, tileX, tileY, selection, button):
        newTile = "water"
        if button == 1:
            newTile = self.palette.tileName
        minX = min(selection[0].x, selection[1].x)
        minY = min(selection[0].y, selection[1].y)
        maxX = max(selection[0].x, selection[1].x)
        maxY = max(selection[0].y, selection[1].y)
        self._fill(tileX, tileY, minX, minY, maxX, maxY, newTile)
            
    def do_fillTerrain(self, tileX, tileY, button):
        newTile = "water"
        if button == 1:
            newTile = self.palette.tileName
        self._fill(tileX, tileY, -10000, -10000, 10000, 10000, newTile)
    
    def do_fillAll(self, minmax, button):
        tiles = self.game.ocean.tiles
        newTile = self.palette.tileName
        if newTile == "water" or button == 3:
            ab = AxisAlignedBoundingBox(*minmax)
            for (col,row) in ab.enumerate():
                if (col,row) in tiles:
                    del tiles[(col,row)]
        elif button == 1:
            ab = AxisAlignedBoundingBox(*minmax)
            for (col,row) in ab.enumerate():
                tiles[(col,row)] = newTile
        self.ocean.dirty = True
        self.map.dirty = True
    
    def onMouseDownSelection(self, event, tileX, tileY):
        retval = None
        modkeys = pygame.key.get_mods()

        if self.selection[2].y <= self.selection[3].y:
            pmin = Point(self.selection[2].x,
                         self.selection[3].y)
            pmax = Point(self.selection[3].x + 1,
                         self.selection[2].y - 1)
        else:
            pmin = self.selection[2]
            pmax = self.selection[3] + Point(1,-1)
            
        minX, minY = self.map.getTilePos(pmin).pos
        maxX, maxY = self.map.getTilePos(pmax).pos
        
        if (minX <= event.pos[0] <= maxX and
            minY <= event.pos[1] <= maxY):
            if modkeys & KMOD_CTRL:
                self.do_fillAll(self.selection[0:2], event.button)
            else:
                self.do_fillSelection(tileX, tileY,
                                      self.selection[0:2],
                                      event.button)
            retval = False
            
        elif event.button == 3:
            self.edit.clearSelection()
            retval = False
            
        return retval
    
    def onEvent(self, event):
        retval = None
        if not (hasattr(event, 'pos') and
                self.window.checkOverlayClick(event.pos)):
            pass

        elif event.type == MOUSEBUTTONDOWN:
            self.selection = self.edit.selection
            tileX, tileY = self.map.getMousePos(grid=False)
            if len(self.selection) == 4 and event.button in (1,3):
                
                retval = self.onMouseDownSelection(event, tileX, tileY)
                
            elif event.button == 1:
                oldTile = self.game.ocean.tiles.get((tileX, tileY),"water")
                if oldTile != "water":
                    self.do_fillTerrain(tileX, tileY, event.button)
                    retval = False
                else:
                    self.window.dialogMessage("Cannot fill water" +
                                              "without bounding selection.")
                    retval = False
                
            elif event.button == 2:
                tileName = self.game.ocean.tiles.get((tileX, tileY), "water")
                self.palette.selectPageAndTile(tileName)
                retval = False
            
            elif event.button == 3:
                oldTile = self.game.ocean.tiles.get((tileX, tileY),"water")
                if oldTile != "water":
                    self.do_fillTerrain(tileX, tileY, event.button)
                    retval = False
        
        return retval
                
    
    def onDrawMapObjects(self, surface):
        self.edit.drawMarchingAnts(surface)
